1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.system;
12 
13 import hip.util.conv;
14 import hip.util.string:fromStringz, toStringz;
15 import hip.util.path:pathSeparator;
16 
17 version(PSVita) version = NoSharedLibrarySupport;
18 version(WebAssembly) version = NoSharedLibrarySupport;
19 version(CustomRuntimeTest) version = NoSharedLibrarySupport;
20 version(Android) version = NoSharedLibrarySupport;
21 version(iOS) version = NoSharedLibrarySupport;
22 
23  version(Windows)
24 {
25     pragma(lib, "psapi.lib");
26     pragma(lib, "dbghelp.lib");
27     private struct MODLOAD_DATA {DWORD ssize; DWORD ssig; PVOID data; DWORD size; DWORD flags; }
28     import hip.util.windows;
29     extern(Windows) private 
30     {
31         alias SymUnloadModule = SymUnloadModule64;
32         BOOL SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
33         DWORD64 SymLoadModuleEx(
34             HANDLE        hProcess,
35             HANDLE        hFile,
36             PCSTR         ImageName,
37             PCSTR         ModuleName,
38             DWORD64       BaseOfDll,
39             DWORD         DllSize,
40             MODLOAD_DATA* Data,
41             DWORD         Flags
42         );
43         BOOL SymRefreshModuleList(HANDLE hProcess);
44 
45     }
46 }
47 enum debugger = "asm {int 3;}";
48 
49 string sanitizePath(string path) @safe pure nothrow
50 {
51     version(Windows)
52         return path;
53     else
54     {
55         import hip.util.string;
56         if(indexOf(path, '\\') == -1)
57             return path;
58         return replaceAll(path, '\\', "/");
59     }
60 }
61 bool isPathUnixStyle(string path) @safe pure nothrow 
62 {
63     for(size_t i = 0; i < path.length; i++)
64         if(path[i] == '/')
65             return true;
66     return false;
67 }
68 string buildPath(string[] args...) @safe pure nothrow
69 {
70     if(args.length == 0)
71         return null;
72     string ret;
73     for(int i = 0; i < cast(int)args.length-1; i++)
74         ret~= args[i]~pathSeparator;
75     return ret~args[$-1];
76 }
77 
78 version(Windows)
79 {
80     // import core.sys.windows.winbase;
81     // import core.sys.windows.windef;
82 
83     import hip.util.windows;
84 
85     private HMODULE moduleHandle;
86     extern(Windows) nothrow @system void* dll_import_var(string name)
87     {
88         if(moduleHandle == null)
89             moduleHandle = GetModuleHandle(null);
90         return GetProcAddress(moduleHandle, (name~"\0").ptr);
91     }
92     string[] dllImportVariables(Args...)()
93     {
94         import std.traits:isFunctionPointer;
95         string[] failedFunctions;
96         static foreach(a; Args)
97         {
98             static assert(isFunctionPointer!a, "Can't dll import a non function pointer ( "~a.stringof~" )");
99             a = cast(typeof(a))dll_import_var(a.stringof);
100             if(a is null)
101                 failedFunctions~= a.stringof;
102         }
103         return failedFunctions;
104     }
105 
106     string getLastWindowsErrorMessage()
107     {
108         return getWindowsErrorMessage(GetLastError());
109     }
110 
111     string getWindowsErrorMessage(HRESULT hr)
112     {
113         wchar* buffer;
114         HRESULT fmt = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 
115         FORMAT_MESSAGE_IGNORE_INSERTS |
116         FORMAT_MESSAGE_ALLOCATE_BUFFER,
117         null, hr, 0u, cast(LPWSTR)&buffer, 0, null);
118 
119         if(fmt == 0)
120             return "Error code '"~hip.util.conv.toString(hr)~"' not found";
121         string ret = fromUTF16(cast(wstring)buffer[0..fmt]);
122 
123         LocalFree(buffer);
124 
125         return ret;
126     }
127 
128     void winEnforce(scope BOOL delegate() dg, scope string message)
129     {
130         if(!dg())
131             throw new Error(message~": "~getWindowsErrorMessage(GetLastError()));
132     }
133 }
134 
135 
136 /** 
137  * 
138  * Params:
139  *   libName = Basically a path
140  * Returns: libName appended with the dynamic library extension
141  */
142 string dynamicLibraryGetLibName(string libName)
143 {
144     version(NoSharedLibrarySupport) return "";
145     else version(Windows) return libName~".dll";
146     else version(Posix)
147     {
148         import hip.util.path;
149         libName.filename = "lib"~libName.filename~".so";
150         return libName;
151     }
152     else static assert(0, "Platform not supported");
153 }
154 
155 bool dynamicLibraryIsLibNameValid(string libName)
156 {
157     version(NoSharedLibrarySupport)
158         return true;
159     else version(Windows)
160         return libName[$-4..$] == ".dll";
161     else version(Posix)
162     {
163         import hip.util.path;
164         return libName.filename[0..3] == "lib" && libName[$-3..$] == ".so";
165     }
166 }
167 
168 version(NoSharedLibrarySupport)
169 {
170     void* dynamicLibraryLoad(string libName) { return null; }
171 }
172 else
173 ///It will open the current executable if libName == null
174 void* dynamicLibraryLoad(string libName)
175 {
176     void* ret;
177     if(libName == null)
178     {
179         version(Windows)
180         {
181             ret = GetModuleHandle(null);
182         }
183         else version(linux)
184         {
185             import core.sys.posix.dlfcn : dlopen, RTLD_NOW;
186             ret = dlopen(null, RTLD_NOW);
187         }
188     }
189     else 
190     {
191         version (OSX) {}  //OSX gives an error with rt_loadLibrary not found.
192         else
193         {
194             import core.runtime;
195             ret = Runtime.loadLibrary(libName);
196             version(Windows)
197             {
198                 import core.sys.windows.psapi;
199                 import core.sys.windows.winbase;
200                 MODULEINFO moduleInfo;
201 
202                 winEnforce(() => GetModuleInformation(GetCurrentProcess(), ret, &moduleInfo, MODULEINFO.sizeof), "Could not get module information");
203                 if(!SymLoadModuleEx(GetCurrentProcess(), null, libName.toStringz, null, cast(ulong)moduleInfo.lpBaseOfDll, moduleInfo.SizeOfImage, null, 0))
204                 {
205                     import core.sys.windows.winerror;
206 
207                     HRESULT lastErr = GetLastError();
208                     if(lastErr != ERROR_SUCCESS)
209                     {
210                         import core.sys.windows.winuser;
211                         MessageBoxA(null, toStringz("Failed to load the DLL named "~libName~" pdb symbols "~getWindowsErrorMessage(lastErr) ~ " " ~ lastErr.to!string), "PDB Loading Failure", MB_ICONERROR | MB_OK);
212                     }
213                 }
214 
215             }
216         }
217     }
218     return ret;
219 }
220 
221 version(Windows) private const (char)* err;
222 void* dynamicLibrarySymbolLink(void* dll, const (char)* symbolName)
223 {
224     void* ret;
225     version(NoSharedLibrarySupport)
226         ret = null;
227     else version(Windows)
228     {
229         ret = GetProcAddress(dll, symbolName);
230         if(!ret)
231             err = ("Could not link symbol "~symbolName.fromStringz).ptr;
232     }
233     else version(Posix)
234     {
235         import core.sys.posix.dlfcn : dlsym;
236         ret = dlsym(dll, symbolName);
237     }
238     return ret;
239 }
240 
241 
242 string dynamicLibraryError()
243 {
244     version(NoSharedLibrarySupport)
245         return "Current platform does not load dynamic libraries";
246     else version(Windows)
247     {
248         const(char)* ret = err;
249         err = null;
250         return cast(string)fromStringz(ret);
251     }
252     else version(Posix)
253     {
254         import core.sys.posix.dlfcn;
255         return cast(string)fromStringz(dlerror());
256     }
257     else static assert(0, "Platform not supported");
258 }
259 
260 bool dynamicLibraryRelease(void* dll)
261 {
262     version(NoSharedLibrarySupport)
263         return false;
264     else version(UWP)
265     {
266         return cast(bool)FreeLibrary(dll);
267     }
268     else version(Posix)
269     {
270         import core.sys.posix.dlfcn:dlclose;
271         return cast(bool)dlclose(dll);
272     }
273     else
274     {
275         import core.runtime;
276         version(Windows)
277         {
278             import core.sys.windows.winbase;
279             import core.sys.windows.psapi;
280             MODULEINFO moduleInfo;
281             winEnforce(() => GetModuleInformation(GetCurrentProcess(), dll, &moduleInfo, MODULEINFO.sizeof), "Could not get module information");
282             winEnforce(() => SymUnloadModule(GetCurrentProcess(), cast(ulong)moduleInfo.lpBaseOfDll), "Could not unload PDB");
283         }
284         return Runtime.unloadLibrary(dll);
285     }
286 }